package com.lnt.spring;

import com.lnt.spring.bean.Role;
import com.lnt.spring.dependencies.CircularServiceTest1;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

/**
 * @title :
 * @createDate : 2021-10-26 09:45
 * @creater : LuNanTing
 * @className : SpringTest
 */
public class SpringTest {

    /**
     *
     * @Title : {}
     * @description:
     * @Author: LuNanTing
     * @Date: 2021/10/26 - 10:06
     * @param: [args]
     * @return: void
     */
    /**
     * 当Spring创建一个bean生命周期，
     * ----->1.通过bean对象无参构造new一个对象(当对象存在多个构造方法时，Spring会默认找无参构造方法，如果定义了一个构造函数，无论是否带参，都只会调用这个构造函数)
     * ----->2.通过方法获取依赖注入的全局变量，并赋值
     * ----->3.初始化前(@PostConstruct)
     * ----->4.初始化(实现 InitializingBean接口，完善实现接口初始方法)
     * ----->5.初始化后(AOP)
     * ----->------->(①)首先判断是否有AOP在该对象内的方法进行了切片，
     * ----->------->------->①找出所有的所有的切面bean,并且遍历所有的切面bean
     * ----->------->------->②找出当前遍历的切面bean下的所有的切面方法，即使用了切面注解的方法
     * ----->------->------->③找到注解中的表达式，是否匹配当前bean对象方法
     * ----->------->------->
     * ----->------->(②)AOP后得到代理对象(如果没有使用aop则不会得到代理对象,返回bean对象)，代理对象会继承该对象，并生成一个该对象的变量，
     * ----->------->(③)生成的该对象的变量定义为target,并且赋值为该对象的普通类,即，target = new T();
     * ----->------->(④)在生成的代理类中会处理切片前，切片后的方法，
     * ----->6.将初始化返回的对象放入单例池Map
     * ----->7.此时得到了bean对象
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        // 非懒加载(没有注明@Lazy)的单例bean是在Spring容器启动的时候创建的
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

        // 原型bean(每次获得的都不一样,用@Scope("prototype")定义),懒加载的单例bean都是在getBean 的时候创建的
//        UserService userService =  applicationContext.getBean("userService", UserService.class);

        // 编程式注册bean
//        programmaticBean(applicationContext);

        // spring提供的资源加载
//        getResource(applicationContext);

        // 获取变量
//        getEnvironment(applicationContext);

        // 将JDK自带的类型转换PropertyEditor纳入Spring容器
//        userService.user();

        // Spring 别名测试
//        aliasTest(applicationContext);

        // 依赖注入测试
//        AutowiredInjectServiceTest autowiredInjectServiceTest = (AutowiredInjectServiceTest)applicationContext.getBean("autowiredInjectServiceTest");
//        System.out.println("依赖注入@Lazy:" + autowiredInjectServiceTest.getUserService());
//        autowiredInjectServiceTest.test();

        // 循环依赖
        circularDependencies(applicationContext);

        // bean 销毁
//        System.out.println("========[Spring 销毁方法Test]========");
//        applicationContext.close();
    }

    /**
     * 在Spring中通过@Bean,@Component,<bean></bean>等方式注册bean的方式称为声明式
     * 在Spring中还存在编程式注册bean,所有的bean会被纳入一个bean的池中，beanName为key，value为一个BeanDefinition
     * 所以首先先声明一个BeanDefinition
     * Spring 底层也是该思路实现的
     */
    public static void programmaticBean(AnnotationConfigApplicationContext applicationContext){

        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        /**
         * 将声明的BeanDefinition赋值属性
         */
        beanDefinition.setBeanClass(Role.class);
        beanDefinition.setScope("prototype");
        beanDefinition.setLazyInit(false);
        /**
         * 将BeanDefinition注册进ApplicationContext的Spring容器中
         */
        applicationContext.registerBeanDefinition("role", beanDefinition);
    }

    public static void getResource(AnnotationConfigApplicationContext applicationContext){
        Resource resource = applicationContext.getResource("classpath:spring.xml");
        try {
            System.out.println(resource.getURL());
            System.out.println(resource.contentLength());
        } catch (IOException e) {
            e.printStackTrace();
        }
        Resource resource1 = applicationContext.getResource("https://www.bing.com");
        try {
            System.out.println(resource1.getURL());
            System.out.println(resource1.contentLength());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取环境变量
     * @param applicationContext
     */
    public static void getEnvironment(AnnotationConfigApplicationContext applicationContext){
        // 获取系统环境变量
        Map<String, Object> systemEnvironment = applicationContext.getEnvironment().getSystemEnvironment();
        for (Map.Entry<String, Object> stringObjectEntry : systemEnvironment.entrySet()) {
            System.out.println("key:" + stringObjectEntry.getKey() + ", value:" + stringObjectEntry.getValue());
        }
        // 获取操作环境变量
        Map<String, Object> systemProperties = applicationContext.getEnvironment().getSystemProperties();
    }

    /**
     * Spring 别名测试
     */
    public static void aliasTest(AnnotationConfigApplicationContext applicationContext){
        System.out.println("======Spring 别名测试======");
        String alias = "aliasBean";
        String[] aliases = applicationContext.getAliases(alias);
        boolean b = applicationContext.containsBeanDefinition(alias);
        BeanFactory parentBeanFactory = applicationContext.getParentBeanFactory();
        System.out.println("is Have :" + b);
        System.out.println("parent is Have :" + parentBeanFactory);
        System.out.println(Arrays.asList(aliases));
        System.out.println("==========================");
    }

    /**
     * 循环依赖
     */
    public static void circularDependencies(AnnotationConfigApplicationContext applicationContext){
        CircularServiceTest1 circularServiceTest1 = (CircularServiceTest1) applicationContext.getBean("circularServiceTest1");
        System.out.println("=============================循环依赖说明");
        System.out.println(circularServiceTest1.toString());
    }
}
